/*
    Copyright (C) 2010-2011 mdis
    
    This file is part of mdis.
    Written by Ketfi-Chérif Mehdi <ketficherif.mehdi@gmail.com>

    mdis is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    mdis is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with mdis.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "global.h"

int main ( int argc , char **argv )
{
	uint8_t i = 0;
	uint32_t flags = 0 , key = 0;
	char ** plugin_directory = NULL;
	plugin_t * plugin = NULL;
	pstatic_text_t title = NULL , status = NULL;
	pmwindow_t wassembly = NULL , wregs = NULL , wdata = NULL;
	ptui_listview_t asmview = NULL , registers = NULL;
	infile_t file; ptui_hexview_t hexview = NULL;
	simul_t * sim = NULL;
	char tmp[256];

	if ( getuid() == 0 )
		flags = (flags&FLAGS_ROOT_USER)|FLAGS_ROOT_USER;

	if ( argc < 2 )
	{
		printf( MDIS_USAGE );
		return 0;
	}

	plugin = plugin_create ();
	plugin_directory = misc_list_directory ();
	if ( !plugin | !plugin_directory )
		return 0;
	if ( plugin_init ( plugin ) == ERROR_ALLOCATE_MEMORY )
	{
		plugin_destroy ( plugin );
		return 0;
	}
	do
	{
		if ( plugin_set_directory ( plugin , plugin_directory[i] , strlen (plugin_directory[i]) ) == ERROR_ALLOCATE_MEMORY )
		{
			misc_list_destroy ( plugin_directory );
			plugin_destroy ( plugin );
			return 0;
		}
		if ( plugin_search_libs ( plugin ) == PLUGIN_FOUND )
		{
			flags = (flags&FLAGS_PLUGIN_FOUND)|FLAGS_PLUGIN_FOUND;
			break;
		}
	} while ( ++i < DEFAULT_NPLUGINS_DIR );
	misc_list_destroy ( plugin_directory );

	tui_init ();
	tui_init_colors ();

	if ( flags&FLAGS_ROOT_USER )
	{
		assume_default_colors ( COLOR_WHITE , COLOR_RED );
		tui_msgbox ( "/!\\ WARNING: This application was not designed to\n"
			     "            run as root user. BE CAREFUL!" );
	}
	if ( !(flags&FLAGS_PLUGIN_FOUND) )
	{
		assume_default_colors ( COLOR_WHITE , COLOR_RED );
		tui_msgbox ( "/!\\ WARNING: There is no plugin!" );
		tui_close ();
		plugin_destroy ( plugin );
		return 0;
	}

	assume_default_colors ( COLOR_WHITE , COLOR_BLACK );
	
	title = tui_static_text_create ( scrmaxx , 1 , 0 , 0 , A_BOLD , C(TUI_TEXT_WHITE_BLUE) , TUI_TEXT_CENTER , MDIS_VERSION );
	status = tui_static_text_create ( scrmaxx , 1 , 0 , scrmaxy , A_BOLD , C(TUI_TEXT_BLACK_WHITE) , TUI_TEXT_LEFT , "Status bar." );
	wassembly = tui_mwindow_create ( 2*scrmaxx/3 , 2*scrmaxy/3 , 0 , 1 , "Assembly view" );
	wregs = tui_mwindow_create ( ((scrmaxx%3) >= 2) ? scrmaxx/3+1 : scrmaxx/3+scrmaxx%3 , 2*scrmaxy/3 , 2*scrmaxx/3 , 1 , "Registers"  );
	wdata = tui_mwindow_create ( scrmaxx , scrmaxy/3+2*scrmaxy%3-1 , 0 , 2*scrmaxy/3+1 , "ROM" );
	asmview = tui_listview_create ( wassembly->win , wassembly->w-2 , wassembly->h-2 , 1 , 1 );
	registers = tui_listview_create ( wregs->win , wregs->w-2 , wregs->h-2 , 1 , 1 );
	hexview = tui_hexview_create ( wdata->win , wdata->w-2 , wdata->h-2 , 1 , 1 );

	refresh ();

	if ( flags&FLAGS_PLUGIN_FOUND )
	{
		switch ( plugin_load_syms ( plugin ) )
		{
			case PLUGIN_LOAD_ERROR:
				tui_msgbox ( "/!\\ Could not load the plugin." );
				flags = (flags&FLAGS_PLUGIN_FOUND)|0x00;
				tui_hexview_destroy ( hexview );
				tui_listview_destroy ( asmview );
				tui_listview_destroy ( registers );
				tui_mwindow_destroy ( wassembly );
				tui_mwindow_destroy ( wregs );
				tui_mwindow_destroy ( wdata );
				tui_static_text_destroy ( title );
				tui_static_text_destroy ( status );
				tui_close ();
				plugin_destroy ( plugin );
				exit (0);
				break;

			case PLUGIN_SYMS_NOT_FOUND:
				tui_msgbox ( "/!\\ Could not find all required symbols!" );
				flags = (flags&FLAGS_PLUGIN_FOUND)|0x00;
				tui_hexview_destroy ( hexview );
				tui_listview_destroy ( asmview );
				tui_listview_destroy ( registers );
				tui_mwindow_destroy ( wassembly );
				tui_mwindow_destroy ( wregs );
				tui_mwindow_destroy ( wdata );
				tui_static_text_destroy ( title );
				tui_static_text_destroy ( status );
				tui_close ();
				plugin_destroy ( plugin );
				exit (0);
				break;
		}

		for ( i = 0; i < plugin->pdesc->nregs ; i++ )
		{
			sprintf ( tmp , "%s\t: %02Xh 00000000b" , plugin->pdesc->regname[i] , plugin->funcs->get_reg (i) );
			tui_listview_add_item ( registers , tmp );
		}

		tui_listview_set_focus ( asmview , 1 );
		file.fname = argv[argc-1];
		open_hex ( &file );
		plugin->funcs->init ( file.binbuf , get_nbytes_hex ( &file ) );
		uint8_t * code = plugin->funcs->get_memcode ();
		close_hex ( &file );
		plugin_disasm ( plugin , asmview , code , 32768 );
		tui_hexview_set ( hexview , code , 32768 );
		mvwprintw ( status->win , 0 , 0 , "Offset: %04Xh Byte: %02Xh Word: %02X%02Xh Dword: "
			"%02X%02X%02X%02Xh" , 
			hexview->curs , hexview->data[hexview->curs] , hexview->data[hexview->curs] ,
			(hexview->curs+1<hexview->nbytes)?hexview->data[hexview->curs+1]:0x00 ,
			hexview->data[hexview->curs] ,
			(hexview->curs+1<hexview->nbytes)?hexview->data[hexview->curs+1]:0x00 , 
			(hexview->curs+2<hexview->nbytes)?hexview->data[hexview->curs+2]:0x00 ,
			(hexview->curs+3<hexview->nbytes)?hexview->data[hexview->curs+3]:0x00 );
	}

	tui_static_text_show ( title );
	tui_static_text_show ( status );
	tui_mwindow_show ( wassembly );
	tui_mwindow_show ( wregs );
	tui_mwindow_show ( wdata );
	tui_listview_show ( asmview );
	tui_listview_show ( registers );
	tui_hexview_show ( hexview );

	while ( (key = getch()) != 'q' )
	{
		switch ( key )
		{
			case KEY_TAB:
				if ( registers->focus )
				{
					tui_listview_set_focus ( registers , 0 );
					tui_listview_set_focus ( asmview , 0 );
					tui_hexview_set_focus ( hexview , 1 );
				}
				else if ( asmview->focus )
				{
					tui_listview_set_focus ( asmview , 0 );
					tui_hexview_set_focus ( hexview , 0 );
					tui_listview_set_focus ( registers , 1 );
				}
				else if ( hexview->focus )
				{
					tui_listview_set_focus ( registers , 0 );
					tui_hexview_set_focus ( hexview , 0 );
					tui_listview_set_focus ( asmview , 1 );
				}
				break;

			case KEY_DOWN:
				if ( registers->focus )
					tui_listview_select_next ( registers );
				else if ( asmview->focus )
					tui_listview_select_next ( asmview );
				else if ( hexview->focus )
					tui_hexview_down ( hexview );
				break;

			case KEY_UP:
				if ( registers->focus )
					tui_listview_select_prev ( registers );
				else if ( asmview->focus )
					tui_listview_select_prev ( asmview );
				else if ( hexview->focus )
					tui_hexview_up ( hexview );
				break;

			case KEY_RIGHT:
				if ( hexview->focus )
					tui_hexview_right ( hexview );
				break;

			case KEY_LEFT:
				if ( hexview->focus )
					tui_hexview_left ( hexview );
				break;

			case KEY_NPAGE:
				if ( registers->focus )
					tui_listview_select_snext ( registers );
				else if ( asmview->focus )
					tui_listview_select_snext ( asmview );
				break;

			case KEY_PPAGE:
				if ( registers->focus )
					tui_listview_select_sprev ( registers );
				else if ( asmview->focus )
					tui_listview_select_sprev ( asmview );
				break;

			case '1':
				if ( hexview->focus )
				{
					tui_mwindow_set_title ( wdata , "ROM" );
					tui_hexview_set ( hexview , plugin->funcs->get_memcode() , 32768 );
				}
				break;

			case '2':
				if ( hexview->focus )
				{
					tui_mwindow_set_title ( wdata , "Internal RAM" );
					tui_hexview_set ( hexview , plugin->funcs->get_memram() , 256 );
				}
				break;

			case '3':
				if ( hexview->focus )
				{
					tui_mwindow_set_title ( wdata , "External RAM" );
					tui_hexview_set ( hexview , plugin->funcs->get_memxram() , 32768 );
				}
				break;

			case 't':
				sim = plugin->funcs->simul ();
				break;
		}
		mvwprintw ( status->win , 0 , 0 , "Offset: %04Xh Byte: %02Xh Word: %02X%02Xh Dword: "
				"%02X%02X%02X%02Xh" , 
				hexview->curs , hexview->data[hexview->curs] , hexview->data[hexview->curs] ,
				(hexview->curs+1<hexview->nbytes)?hexview->data[hexview->curs+1]:0x00 ,
				hexview->data[hexview->curs] ,
				(hexview->curs+1<hexview->nbytes)?hexview->data[hexview->curs+1]:0x00 , 
				(hexview->curs+2<hexview->nbytes)?hexview->data[hexview->curs+2]:0x00 ,
				(hexview->curs+3<hexview->nbytes)?hexview->data[hexview->curs+3]:0x00 );
		if ( sim != NULL )
			mvwprintw ( status->win , 0 , status->w-9 , "PC[%04X]" , sim->pc );
		for ( i = 0; i < plugin->pdesc->nregs ; i++ )
		{
			byte_to_bin ( plugin->funcs->get_reg (i) , tmp );
			sprintf ( registers->items[i] , "%s\t: %02Xh %sb" , plugin->pdesc->regname[i] , plugin->funcs->get_reg (i) , tmp );
		}
		tui_static_text_show ( title );
		tui_static_text_show ( status );
		tui_mwindow_show ( wassembly );
		tui_mwindow_show ( wregs );
		tui_mwindow_show ( wdata );
		tui_listview_show ( asmview );
		tui_listview_show ( registers );
		tui_hexview_show ( hexview );
		refresh ();
	}

	tui_hexview_destroy ( hexview );
	tui_listview_destroy ( asmview );
	tui_listview_destroy ( registers );
	tui_mwindow_destroy ( wassembly );
	tui_mwindow_destroy ( wregs );
	tui_mwindow_destroy ( wdata );
	tui_static_text_destroy ( title );
	tui_static_text_destroy ( status );
	tui_close ();
	plugin_destroy ( plugin );
	return 0;
}

